package com.copd.aop;

import com.copd.service.DoctorOperationLogService;
import com.copd.util.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class OperationLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private DoctorOperationLogService logService;

    @Autowired
    private JwtUtil jwtUtil;

    // 定义切点：拦截所有患者管理相关的控制器方法
    @Pointcut("execution(* com.copd.controller.PatientController.*(..))")
    public void patientControllerPointcut() {}

    // 定义切点：拦截所有操作日志相关的控制器方法
    @Pointcut("execution(* com.copd.controller.DoctorOperationLogController.*(..))")
    public void operationLogControllerPointcut() {}

    // 方法成功执行后记录日志
    @AfterReturning(pointcut = "patientControllerPointcut() || operationLogControllerPointcut()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        try {
            // 获取请求信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }

            HttpServletRequest request = attributes.getRequest();
            
            // 获取方法签名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            
            // 获取医生ID
            Integer doctorId = getDoctorIdFromRequest(request);
            if (doctorId == null) {
                // 如果未能获取有效的doctorId，则跳过日志记录
                logger.warn("无法获取有效的doctorId，跳过操作日志记录");
                return;
            }
            
            // 获取患者ID (如果存在)
            Integer patientId = getPatientIdFromRequest(request, joinPoint.getArgs());
            
            // 确定操作类型
            String operationType = determineOperationType(request.getMethod(), method.getName());
            
            // 获取请求IP和设备信息
            String ipAddress = getClientIpAddress(request);
            String deviceInfo = request.getHeader("User-Agent");
            
            // 创建操作详情JSON
            String detail = createOperationDetail(request, joinPoint.getArgs(), result);
            
            // 记录日志
            logService.logPatientOperation(
                doctorId,
                patientId,
                operationType,
                ipAddress,
                deviceInfo,
                true, // 成功
                null, // 无失败原因
                determineTableName(method.getName()),
                patientId,
                detail
            );
        } catch (Exception e) {
            logger.error("记录操作日志失败", e);
            // 不抛出异常，避免影响正常业务流程
        }
    }

    // 方法抛出异常后记录日志
    @AfterThrowing(pointcut = "patientControllerPointcut() || operationLogControllerPointcut()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
        try {
            // 获取请求信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }

            HttpServletRequest request = attributes.getRequest();
            
            // 获取方法签名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            
            // 获取医生ID
            Integer doctorId = getDoctorIdFromRequest(request);
            if (doctorId == null) {
                // 如果未能获取有效的doctorId，则跳过日志记录
                logger.warn("无法获取有效的doctorId，跳过操作日志记录");
                return;
            }
            
            // 获取患者ID (如果存在)
            Integer patientId = getPatientIdFromRequest(request, joinPoint.getArgs());
            
            // 确定操作类型
            String operationType = determineOperationType(request.getMethod(), method.getName());
            
            // 获取请求IP和设备信息
            String ipAddress = getClientIpAddress(request);
            String deviceInfo = request.getHeader("User-Agent");
            
            // 创建操作详情JSON，包含异常信息
            Map<String, Object> detailMap = new HashMap<>();
            detailMap.put("method", request.getMethod());
            detailMap.put("uri", request.getRequestURI());
            detailMap.put("methodName", method.getName());
            detailMap.put("exceptionName", ex.getClass().getName());
            detailMap.put("exceptionMessage", ex.getMessage());
            String detail = objectMapper.writeValueAsString(detailMap);
            
            // 记录日志
            logService.logPatientOperation(
                doctorId,
                patientId,
                operationType,
                ipAddress,
                deviceInfo,
                false, // 失败
                ex.getMessage(), // 失败原因
                determineTableName(method.getName()),
                patientId,
                detail
            );
        } catch (Exception e) {
            logger.error("记录操作日志失败", e);
            // 不抛出异常，避免影响正常业务流程
        }
    }

    // 从请求中获取医生ID
    private Integer getDoctorIdFromRequest(HttpServletRequest request) {
        try {
            // 先尝试从请求属性中获取
            Object doctorIdObj = request.getAttribute("doctorId");
            if (doctorIdObj != null) {
                return (Integer) doctorIdObj;
            }
            
            // 如果请求属性中没有，尝试从JWT令牌中获取
            String authHeader = request.getHeader("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                String token = authHeader.substring(7);
                return jwtUtil.getUserIdFromToken(token);
            }
        } catch (Exception e) {
            logger.error("获取医生ID失败", e);
        }
        return null;
    }

    // 从请求中获取患者ID
    private Integer getPatientIdFromRequest(HttpServletRequest request, Object[] args) {
        try {
            // 1. 尝试从URL路径中获取
            String uri = request.getRequestURI();
            String[] pathSegments = uri.split("/");
            if (pathSegments.length >= 3 && pathSegments[2].equals("patients") && pathSegments.length >= 4) {
                try {
                    // 处理类似 /api/patients/{patientId} 的情况
                    return Integer.parseInt(pathSegments[3]);
                } catch (NumberFormatException e) {
                    // 不是数字，继续其他方式尝试
                }
            }
            
            // 2. 尝试从方法参数中获取
            if (args != null) {
                for (Object arg : args) {
                    if (arg instanceof Integer) {
                        // 假设第一个Integer类型参数可能是patientId
                        return (Integer) arg;
                    }
                }
            }
        } catch (Exception e) {
            logger.error("获取患者ID失败", e);
        }
        return null;
    }

    // 确定操作类型
    private String determineOperationType(String httpMethod, String methodName) {
        if ("GET".equalsIgnoreCase(httpMethod)) {
            return "查询";
        } else if ("POST".equalsIgnoreCase(httpMethod)) {
            return "新增";
        } else if ("PUT".equalsIgnoreCase(httpMethod)) {
            return "修改";
        } else if ("DELETE".equalsIgnoreCase(httpMethod)) {
            return "删除";
        } else {
            // 基于方法名推断
            if (methodName.startsWith("get") || methodName.startsWith("query") || methodName.startsWith("select")) {
                return "查询";
            } else if (methodName.startsWith("add") || methodName.startsWith("create") || methodName.startsWith("insert")) {
                return "新增";
            } else if (methodName.startsWith("update") || methodName.startsWith("modify")) {
                return "修改";
            } else if (methodName.startsWith("delete") || methodName.startsWith("remove")) {
                return "删除";
            }
        }
        return "其他";
    }

    // 确定操作的表名
    private String determineTableName(String methodName) {
        if (methodName.contains("Patient")) {
            return "patient_info";
        } else if (methodName.contains("Diagnosis")) {
            return "diagnosis_records";
        } else if (methodName.contains("Examination")) {
            return "examination_items";
        } else if (methodName.contains("Treatment")) {
            return "treatment_plans";
        } else if (methodName.contains("Discharge")) {
            return "discharge_records";
        } else if (methodName.contains("Log")) {
            return "doctor_operation_log";
        }
        return "patient_info"; // 默认
    }

    // 创建操作详情JSON
    private String createOperationDetail(HttpServletRequest request, Object[] args, Object result) throws Exception {
        Map<String, Object> detailMap = new HashMap<>();
        detailMap.put("method", request.getMethod());
        detailMap.put("uri", request.getRequestURI());
        
        // 添加请求参数，并分析提取具体表字段
        if (args != null && args.length > 0) {
            Map<String, Object> argsMap = new HashMap<>();
            // 检查请求URL以识别操作的表
            String uri = request.getRequestURI();
            String tableName = determineTableNameFromUri(uri);
            
            for (Object arg : args) {
                if (arg != null && isSerializable(arg)) {
                    // 提取参数中的主要属性
                    if (arg instanceof Map) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> argMap = (Map<String, Object>) arg;
                        for (Map.Entry<String, Object> entry : argMap.entrySet()) {
                            if (entry.getValue() != null) {
                                argsMap.put(entry.getKey(), entry.getValue());
                            }
                        }
                    } else {
                        // 尝试通过反射提取实体类属性
                        extractEntityFields(arg, argsMap);
                    }
                }
            }
            
            // 解析请求参数，确保与正确的表字段对应
            enrichArgsWithTableFields(argsMap, tableName);
            
            detailMap.put("args", argsMap);
        }
        
        // 如果是GET请求，从请求参数中收集信息
        if ("GET".equalsIgnoreCase(request.getMethod())) {
            Map<String, Object> queryParams = new HashMap<>();
            request.getParameterMap().forEach((key, values) -> {
                if (values != null && values.length > 0) {
                    queryParams.put(key, values.length == 1 ? values[0] : values);
                }
            });
            if (!queryParams.isEmpty()) {
                detailMap.put("queryParams", queryParams);
            }
        }
        
        // 添加操作结果摘要
        if (result != null) {
            try {
                if (result instanceof Map) {
                    // 对于Map类型结果，添加关键字段
                    Map<?, ?> resultMap = (Map<?, ?>) result;
                    Map<String, Object> resultInfo = new HashMap<>();
                    
                    if (resultMap.containsKey("success")) {
                        resultInfo.put("success", resultMap.get("success"));
                    }
                    
                    if (resultMap.containsKey("data")) {
                        Object data = resultMap.get("data");
                        if (data instanceof Map) {
                            Map<?, ?> dataMap = (Map<?, ?>) data;
                            if (dataMap.containsKey("total")) {
                                resultInfo.put("totalRecords", dataMap.get("total"));
                            }
                        }
                    }
                    
                    detailMap.put("result", resultInfo);
                }
            } catch (Exception e) {
                // 忽略，不影响主流程
                logger.debug("解析结果数据失败", e);
            }
        }
        
        return objectMapper.writeValueAsString(detailMap);
    }
    
    /**
     * 通过反射提取实体类字段
     */
    private void extractEntityFields(Object entity, Map<String, Object> argsMap) {
        try {
            Class<?> clazz = entity.getClass();
            
            // 检查是否为常见实体类
            if (clazz.getName().startsWith("com.copd.entity") || 
                clazz.getName().startsWith("com.copd.dto")) {
                
                for (java.lang.reflect.Method method : clazz.getMethods()) {
                    String methodName = method.getName();
                    
                    // 处理getter方法
                    if (methodName.startsWith("get") && 
                        !methodName.equals("getClass") && 
                        method.getParameterCount() == 0) {
                        
                        String fieldName = methodName.substring(3, 4).toLowerCase() + 
                                          (methodName.length() > 4 ? methodName.substring(4) : "");
                        
                        try {
                            Object value = method.invoke(entity);
                            if (value != null) {
                                argsMap.put(fieldName, value);
                            }
                        } catch (Exception e) {
                            // 忽略反射调用异常
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.debug("提取实体字段失败", e);
        }
    }
    
    /**
     * 根据表名丰富参数映射
     */
    private void enrichArgsWithTableFields(Map<String, Object> argsMap, String tableName) {
        // 根据不同表补充字段描述信息
        Map<String, String> fieldDescriptions = new HashMap<>();
        
        switch (tableName) {
            case "patient_info":
                // 患者基本信息表字段
                fieldDescriptions.put("patientId", "患者ID");
                fieldDescriptions.put("name", "患者姓名");
                fieldDescriptions.put("gender", "性别");
                fieldDescriptions.put("age", "年龄");
                fieldDescriptions.put("department", "科室");
                fieldDescriptions.put("hospitalizationNumber", "住院号");
                break;
                
            case "diagnosis_records":
                // 诊断记录表字段
                fieldDescriptions.put("recordId", "诊断记录ID");
                fieldDescriptions.put("patientId", "患者ID");
                fieldDescriptions.put("diagnosisItem", "诊断项目");
                fieldDescriptions.put("diagnosisContent", "诊断内容");
                fieldDescriptions.put("remarks", "备注");
                fieldDescriptions.put("diagnosisDate", "诊断日期");
                break;
                
            case "examination_items":
                // 检查项目表字段
                fieldDescriptions.put("examId", "检查记录ID");
                fieldDescriptions.put("patientId", "患者ID");
                fieldDescriptions.put("examCategory", "检查类别");
                fieldDescriptions.put("examItem", "检查项目");
                fieldDescriptions.put("examResult", "检查结果");
                fieldDescriptions.put("referenceValue", "参考值范围");
                fieldDescriptions.put("remarks", "备注");
                fieldDescriptions.put("examDate", "检查日期");
                break;
                
            case "treatment_plans":
                // 治疗方案表字段
                fieldDescriptions.put("planId", "治疗方案ID");
                fieldDescriptions.put("patientId", "患者ID");
                fieldDescriptions.put("planCategory", "治疗类别");
                fieldDescriptions.put("treatmentItem", "治疗项目");
                fieldDescriptions.put("specificPlan", "具体治疗方案");
                fieldDescriptions.put("remarks", "备注");
                fieldDescriptions.put("planDate", "制定日期");
                break;
                
            case "discharge_records":
                // 出院建议表字段
                fieldDescriptions.put("dischargeId", "出院记录ID");
                fieldDescriptions.put("patientId", "患者ID");
                fieldDescriptions.put("adviceCategory", "建议类别");
                fieldDescriptions.put("dischargeItem", "出院建议项目");
                fieldDescriptions.put("dischargeContent", "建议内容");
                fieldDescriptions.put("remarks", "备注");
                fieldDescriptions.put("adviceDate", "建议日期");
                break;
                
            default:
                // 通用字段
                fieldDescriptions.put("id", "ID");
                fieldDescriptions.put("patientId", "患者ID");
                fieldDescriptions.put("name", "名称");
                break;
        }
        
        // 将字段描述信息添加到参数映射中
        Map<String, Object> enrichedArgs = new HashMap<>();
        for (Map.Entry<String, Object> entry : argsMap.entrySet()) {
            String fieldName = entry.getKey();
            Object fieldValue = entry.getValue();
            
            String description = fieldDescriptions.getOrDefault(fieldName, fieldName);
            Map<String, Object> fieldInfo = new HashMap<>();
            fieldInfo.put("value", fieldValue);
            fieldInfo.put("description", description);
            
            enrichedArgs.put(fieldName, fieldInfo);
        }
        
        // 替换原始参数映射
        argsMap.clear();
        argsMap.put("tableName", tableName);
        argsMap.put("fields", enrichedArgs);
    }
    
    /**
     * 从URI确定表名
     */
    private String determineTableNameFromUri(String uri) {
        if (uri.contains("/patients")) {
            return "patient_info";
        } else if (uri.contains("/diagnosis")) {
            return "diagnosis_records";
        } else if (uri.contains("/examination")) {
            return "examination_items";
        } else if (uri.contains("/treatment")) {
            return "treatment_plans";
        } else if (uri.contains("/discharge")) {
            return "discharge_records";
        } else if (uri.contains("/backup")) {
            if (uri.contains("patient_info")) {
                return "patient_info";
            } else if (uri.contains("diagnosis")) {
                return "diagnosis_records";
            } else if (uri.contains("examination")) {
                return "examination_items";
            } else if (uri.contains("treatment")) {
                return "treatment_plans";
            } else if (uri.contains("discharge")) {
                return "discharge_records";
            }
        }
        return "unknown";
    }

    // 检查对象是否可序列化
    private boolean isSerializable(Object obj) {
        try {
            // 尝试简单的toString操作，如果没有异常，认为可序列化
            obj.toString();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // 获取客户端IP地址
    private String getClientIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
} 